"""核心扫描器模块"""
import asyncio
import socket
import struct
import random
import time
from typing import List, Dict, Any, Optional
from rich.console import Console
import scapy.all as scapy

from .bypass import FirewallBypass
from .service_detector import ServiceDetector

console = Console()

class LightScanner:
    """轻量级端口扫描器"""
    
    def __init__(self, stealth_mode=False, bypass_enabled=False, 
                 spoof_ip=None, timeout=3, max_threads=100, verbose=False):
        self.stealth_mode = stealth_mode
        self.bypass_enabled = bypass_enabled
        self.spoof_ip = spoof_ip
        self.timeout = timeout
        self.max_threads = max_threads
        self.verbose = verbose
        
        self.bypass = FirewallBypass() if bypass_enabled else None
        self.detector = ServiceDetector()
        
        # 防火墙检测标志
        self.firewall_detected = False
        
    async def scan_targets(self, targets: List[str], ports: List[int], 
                          progress=None, task=None) -> Dict[str, Any]:
        """扫描多个目标"""
        results = {
            'scan_info': {
                'targets': targets,
                'ports': ports,
                'start_time': time.time(),
                'stealth_mode': self.stealth_mode,
                'bypass_enabled': self.bypass_enabled
            },
            'results': {}
        }
        
        # 创建信号量限制并发
        semaphore = asyncio.Semaphore(self.max_threads)
        
        # 为每个目标创建扫描任务
        tasks = []
        for target in targets:
            for port in ports:
                tasks.append(
                    self.scan_port(semaphore, target, port, progress, task)
                )
        
        # 执行所有扫描任务
        scan_results = await asyncio.gather(*tasks, return_exceptions=True)
        
        # 整理结果
        for result in scan_results:
            if isinstance(result, dict) and result.get('status') == 'open':
                target = result['target']
                port = result['port']
                
                if target not in results['results']:
                    results['results'][target] = []
                
                results['results'][target].append(result)
        
        results['scan_info']['end_time'] = time.time()
        results['scan_info']['duration'] = (
            results['scan_info']['end_time'] - results['scan_info']['start_time']
        )
        
        return results
    
    async def scan_port(self, semaphore: asyncio.Semaphore, target: str, 
                       port: int, progress=None, task=None) -> Dict[str, Any]:
        """扫描单个端口"""
        async with semaphore:
            try:
                # 首次探测
                result = await self._initial_probe(target, port)
                
                # 如果端口开放，进行服务识别
                if result['status'] == 'open':
                    service_info = await self.detector.detect_service(
                        target, port, self.timeout
                    )
                    result.update(service_info)
                
                # 如果检测到防火墙，尝试绕过
                elif self.bypass_enabled and self.firewall_detected:
                    bypass_result = await self.bypass.attempt_bypass(
                        target, port, self.stealth_mode
                    )
                    if bypass_result:
                        result.update(bypass_result)
                
                # 更新进度
                if progress and task:
                    progress.update(task, advance=1)
                
                return result
                
            except Exception as e:
                if self.verbose:
                    console.print(f"[red]扫描 {target}:{port} 时出错: {e}[/red]")
                
                if progress and task:
                    progress.update(task, advance=1)
                
                return {
                    'target': target,
                    'port': port,
                    'status': 'error',
                    'error': str(e)
                }
    
    async def _initial_probe(self, target: str, port: int) -> Dict[str, Any]:
        """初始端口探测"""
        if self.stealth_mode:
            return await self._syn_scan(target, port)
        else:
            return await self._tcp_connect_scan(target, port)
    
    async def _syn_scan(self, target: str, port: int) -> Dict[str, Any]:
        """SYN扫描（隐蔽模式）"""
        try:
            # 构建SYN包
            ip_layer = scapy.IP(dst=target)
            if self.spoof_ip:
                ip_layer.src = self.spoof_ip
            
            tcp_layer = scapy.TCP(dport=port, flags='S', seq=random.randint(1000, 65535))
            packet = ip_layer / tcp_layer
            
            # 发送并接收响应
            responses = scapy.sr(packet, timeout=self.timeout, verbose=0)
            
            if responses and responses[0]:
                response = responses[0][0][1]
                
                # 检查是否有RST标志（防火墙可能重置连接）
                if response.haslayer(scapy.TCP) and response[scapy.TCP].flags & 0x04:
                    self.firewall_detected = True
                    return {
                        'target': target,
                        'port': port,
                        'status': 'filtered',
                        'method': 'syn',
                        'response_time': self.timeout
                    }
                
                # SYN-ACK响应表示端口开放
                elif response.haslayer(scapy.TCP) and response[scapy.TCP].flags & 0x12:
                    # 发送RST关闭连接
                    rst_packet = scapy.IP(dst=target) / scapy.TCP(
                        dport=port, flags='R', seq=response[scapy.TCP].ack
                    )
                    scapy.send(rst_packet, verbose=0)
                    
                    return {
                        'target': target,
                        'port': port,
                        'status': 'open',
                        'method': 'syn',
                        'response_time': self.timeout
                    }
            
            return {
                'target': target,
                'port': port,
                'status': 'closed',
                'method': 'syn',
                'response_time': self.timeout
            }
            
        except Exception as e:
            return {
                'target': target,
                'port': port,
                'status': 'error',
                'method': 'syn',
                'error': str(e)
            }
    
    async def _tcp_connect_scan(self, target: str, port: int) -> Dict[str, Any]:
        """TCP连接扫描"""
        try:
            start_time = time.time()
            
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(target, port),
                timeout=self.timeout
            )
            
            response_time = time.time() - start_time
            
            writer.close()
            await writer.wait_closed()
            
            return {
                'target': target,
                'port': port,
                'status': 'open',
                'method': 'tcp_connect',
                'response_time': round(response_time, 3)
            }
            
        except asyncio.TimeoutError:
            return {
                'target': target,
                'port': port,
                'status': 'filtered',
                'method': 'tcp_connect',
                'response_time': self.timeout
            }
        except ConnectionRefusedError:
            return {
                'target': target,
                'port': port,
                'status': 'closed',
                'method': 'tcp_connect',
                'response_time': 0
            }
        except Exception as e:
            return {
                'target': target,
                'port': port,
                'status': 'error',
                'method': 'tcp_connect',
                'error': str(e)
            }
